#define DDCL_CORE

#include <stdlib.h>
#include <memory.h>
#include <stdio.h>

#include "ddcldson.h"
#include "ddclmalloc.h"

//struct tag_ddcl_Dson {
//    size_t len;
//    size_t cap;
//    size_t iter;
//};

#define DDCL_SIZEOF_DSON 12

static inline ddint32
read_32 (char * p){
    return ((p[0] << 24) & 0xFF000000)
        | ((p[1] << 16) & 0xFF0000)
        | ((p[2] << 8) & 0xFF00)
        | (p[3] & 0xFF) ;
}

static inline dduint32
read_u32 (char * p){
    return ((p[0] << 24) & 0xFF000000)
        | ((p[1] << 16) & 0xFF0000)
        | ((p[2] << 8) & 0xFF00)
        | (p[3] & 0xFF) ;
}

static void
write_32 (char * p, ddint32 v){
    p[0] = (v >> 24) & 0xFF;
    p[1] = (v >> 16) & 0xFF;
    p[2] = (v >> 8) & 0xFF;
    p[3] = v & 0xFF;
}

static void
write_u32 (char * p, dduint32 v){
    p[0] = (v >> 24) & 0xFF;
    p[1] = (v >> 16) & 0xFF;
    p[2] = (v >> 8) & 0xFF;
    p[3] = v & 0xFF;
}

static ddint64
read_64 (char * p){
     return (((ddint64)p[0] << 56) & 0xFF00000000000000)
        | (((ddint64)p[1] << 48) & 0xFF000000000000)
        | (((ddint64)p[2] << 40) & 0xFF0000000000)
        | (((ddint64)p[3] << 32) & 0xFF00000000)
        | (((ddint64)p[4] << 24) & 0xFF000000)
        | (((ddint64)p[5] << 16) & 0xFF0000)
        | (((ddint64)p[6] << 8) & 0xFF00)
        | ((ddint64)p[7] & 0xFF) 
        ;
}

static void
write_64 (char * p, ddint64 v){
    p[0] = (v >> 56) & 0xFF;
    p[1] = (v >> 48) & 0xFF;
    p[2] = (v >> 40) & 0xFF;
    p[3] = (v >> 32) & 0xFF;
    p[4] = (v >> 24) & 0xFF;
    p[5] = (v >> 16) & 0xFF;
    p[6] = (v >> 8) & 0xFF;
    p[7] = v & 0xFF;
}

static double
read_double (char * p){
    ddint64 v = read_64(p);
    return *(double *)&v;
}

static void
write_double (char * p, double v){
    ddint64 i = *(ddint64 *)&v;
    write_64(p, i);
}

DDCLAPI inline ddcl_Dson *
ddcl_new_dson (size_t cap){
    if(cap < (8 + sizeof(ddcl_Dson))){
        cap = 8 + sizeof(ddcl_Dson);
    }
    ddcl_Dson * dson = ddcl_malloc(DDCL_SIZEOF_DSON + cap);
    write_32(dson, DDCL_SIZEOF_DSON);
    write_32(dson + 4, cap);
    write_32(dson + 8, DDCL_SIZEOF_DSON);
    return dson;
}

DDCLAPI inline void
ddcl_free_dson (ddcl_Dson * dson){
    ddcl_free(dson);
}


DDCLAPI inline ddcl_Dson *
ddcl_check_expand_dson (
        ddcl_Dson * dson, size_t ex_cap){
    ddint32 len = read_32(dson);
    ddint32 cap = read_32(dson + 4);
    if(ex_cap > (cap - len)){
        ddint32 ncap = cap;
        if(ncap > 2048){ ncap = 2048; }
        if(ncap < ex_cap){ ncap = ex_cap; }
        ncap = cap + ncap;
        ddcl_Dson * new = (ddcl_Dson *)ddcl_malloc(ncap);
        memcpy(new, dson, len);
        ddcl_free(dson);
        write_32(new + 4, ncap);
        return new;
    }
    return dson;
}

DDCLAPI inline int
ddcl_begin_dson (ddcl_Dson * dson){
    write_32(dson + 8, DDCL_SIZEOF_DSON);
    return 0;
}

DDCLAPI inline int
ddcl_next_dson (ddcl_Dson * dson, ddcl_DsonValue * v){
    ddint32 len = read_32(dson);
    ddint32 iter = read_32(dson + 8);
    if(iter >= len){
        return 0;
    }
    char * b = dson + iter;
    v->type = b[0];
    v->size = 0;
    switch(b[0]){
    case DDCL_DSON_BOOLEAN:
        v->u.integer = b[1];
        write_32(dson + 8, iter + 2);
        break;
    case DDCL_DSON_INTEGER:
        v->u.integer = read_64(b + 1);
        write_32(dson + 8, iter + 9);
        break;
    case DDCL_DSON_NUMBER:
        v->u.number = read_double(b + 1);
        write_32(dson + 8, iter + 9);
        break;
    case DDCL_DSON_STRING:
        v->size = read_u32(b + 1);
        v->u.string =  b + 5;
        write_32(dson + 8, iter + 5 + v->size);
        break;
    case DDCL_DSON_ARRAY:
    case DDCL_DSON_MAP:
        {
            ddcl_Dson * arr = (ddcl_Dson *)(b + 1);
            v->u.dson = arr;
            v->u.dson = arr;
            ddint32 arr_len = read_32(arr);
            write_32(dson + 8, iter + 1 + arr_len);
        }
        break;
    case DDCL_DSON_PTR:
        v->size = read_u32(b + 1);
        v->u.ptr = (void *)read_64(b + 5);
        write_32(dson + 8, iter + 13);
        break;
    case DDCL_DSON_NULL:
        write_32(dson + 8, iter + 1);
        break;
    default:
        return 0;
    }
    return 1;
}

DDCLAPI inline size_t
ddcl_dson_len (ddcl_Dson * dson){
    return read_32(dson);
}

DDCLAPI inline ddcl_Dson *
ddcl_clear_dson (ddcl_Dson * dson){
    write_32(dson, DDCL_SIZEOF_DSON);
    return dson;
}

DDCLAPI inline ddcl_Dson *
ddcl_push_dson_integer (ddcl_Dson * dson, ddint64 num){
    dson = ddcl_check_expand_dson(dson, 9);
    ddint32 len = read_32(dson);
    char * b = (char *)dson + len;
    b[0] = DDCL_DSON_INTEGER;
    write_64(b + 1, num);
    write_32(dson, len + 9);
    return dson;
}

DDCLAPI ddcl_Dson *
ddcl_push_dson_boolean (ddcl_Dson * dson, int v){
    dson = ddcl_check_expand_dson(dson, 2);
    ddint32 len = read_32(dson);
    char * b = (char *)dson + len;
    b[0] = DDCL_DSON_BOOLEAN;
    b[1] = (char)v;
    write_32(dson, len + 2);
    return dson;
}

DDCLAPI inline ddcl_Dson *
ddcl_push_dson_number (ddcl_Dson * dson, double num){
    dson = ddcl_check_expand_dson(dson, 9);
    ddint32 len = read_32(dson);
    char * b = (char *)dson + len;
    b[0] = DDCL_DSON_NUMBER;
    write_double(b + 1, num);
    write_32(dson, len + 9);
    return dson;
}

DDCLAPI inline ddcl_Dson *
ddcl_push_dson_string (
        ddcl_Dson * dson, const char * str, dduint32 size){
    dson = ddcl_check_expand_dson(dson, 5 + size);
    ddint32 len = read_32(dson);
    char * b = (char *)dson + len;
    b[0] = DDCL_DSON_STRING;
    write_u32(b + 1, size);
    if(size){ memcpy(b + 5, str, size); }
    write_32(dson, len + 5 + size);
    return dson;
}

DDCLAPI inline ddcl_Dson *
ddcl_push_dson_array (
        ddcl_Dson * dson, ddcl_Dson * array){
    ddint32 arr_len = read_32(array);
    size_t alen = 1 + arr_len;
    dson = ddcl_check_expand_dson(dson, alen);
    ddint32 len = read_32(dson);
    char * b = (char *)dson + len;
    b[0] = DDCL_DSON_ARRAY;
    memcpy(b + 1, array, arr_len);
    write_32(dson, len + alen);
    return dson;
}

DDCLAPI inline ddcl_Dson *
ddcl_push_dson_map (
        ddcl_Dson * dson, ddcl_Dson * array){
    ddint32 arr_len = read_32(array);
    ddint32 len = read_32(dson);
    size_t alen = 1 + arr_len;
    dson = ddcl_check_expand_dson(dson, alen);
    char * b = (char *)dson + len;
    b[0] = DDCL_DSON_MAP;
    memcpy(b + 1, array, arr_len);
    write_32(dson, len + alen);
    return dson;
}


DDCLAPI inline ddcl_Dson *
ddcl_push_dson_ptr (
        ddcl_Dson * dson, void * ptr, dduint32 size){
    dson = ddcl_check_expand_dson(dson, 13);
    ddint32 len = read_32(dson);
    char * b = (char *)dson + len;
    b[0] = DDCL_DSON_PTR;
    write_u32(b + 1, size);
    write_64(b + 5, (ddint64)ptr);
    write_32(dson, len + 13);
    return dson;

}

DDCLAPI inline ddcl_Dson *
ddcl_push_dson_null (ddcl_Dson * dson){
    dson = ddcl_check_expand_dson(dson, 1);
    ddint32 len = read_32(dson);
    char * b = (char *)dson + len;
    b[0] = DDCL_DSON_NULL;
    write_32(dson, len + 1);
    return dson;
}
